package cn.mayaslight.common.utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import cn.mayaslight.common.exception.HandleException;

public class ReflectUtils {

	public static <T> T newInstance(Class<T> clazz, Object... params) {
		try {
			Constructor<T> constructor = clazz.getDeclaredConstructor();
			constructor.setAccessible(true);
			return constructor.newInstance(params.length == 0 ? null : params);
		} catch (Exception e) {
			throw new HandleException(e);
		}
	}
	
	public static Object get(Class<?> targetClass, Object obj, String propertyName) {
		try{
			Field targetField = targetClass.getDeclaredField(propertyName);
			targetField.setAccessible(true);
			return targetField.get(obj);
		} catch (Exception e) {
			throw new HandleException(e);
		}
	}
	
	public static Object invovke(Class<?> targetClass, Object obj, String propertyName) {
		try {
			Method targetMethod = targetClass.getDeclaredMethod(propertyName);
			targetMethod.setAccessible(true);
			return targetMethod.invoke(obj);
		} catch (Exception e) {
			throw new HandleException(e);
		}
	}
	
	public static void set(Class<?> targetClass, Object obj, String propertyName, Object propertyValue) {
		try {
			Field targetField = targetClass.getDeclaredField(propertyName);
			targetField.setAccessible(true);
			targetField.set(obj, propertyValue);
		} catch (Exception e) {
			throw new HandleException(e);
		}
	}
	
	public static <T, A extends Annotation> Map<String, Map<String, Object>> getProperties(Class<? extends T> targetClass, T target, Class<A> anno) {
		return getProperties(targetClass, target, anno, "value");
	}
	
	public static <T, A extends Annotation> Map<String, Map<String, Object>> getProperties(Class<? extends T> targetClass, T target, Class<A> anno, boolean requiredAnno) {
		return getProperties(targetClass, target, anno, requiredAnno, "value");
	}
	
	public static <T, A extends Annotation> Map<String, Map<String, Object>> getProperties(Class<? extends T> targetClass, T target, Class<A> anno, String... annotationKeys) {
		return getProperties(targetClass, target, anno, false, annotationKeys);
	}
	
	/**
	 * 获取属性中注解的值(key-value)
	 * @param targetClass
	 * @param target
	 * @param anno
	 * @param annotationKey
	 * @return
	 */
	public static <T, A extends Annotation> Map<String, Map<String, Object>> getProperties(Class<? extends T> targetClass, T target, Class<A> anno, boolean requiredAnno, String... annotationKeys) {
		Field[] fields = targetClass.getDeclaredFields();
		Map<String, Map<String, Object>> properties = new HashMap<String, Map<String, Object>>();
		OUT:
		for(int i = 0; fields != null && i < fields.length; i++){
			Field subField = fields[i];
			subField.setAccessible(true);
			String key = subField.getName();
			Object fieldValue = get(targetClass, target, key);
			Map<String, Object> annoMap = new HashMap<String, Object>();
			annoMap.put(key, fieldValue);
			if(anno != null){
				if(subField.isAnnotationPresent(anno)){
					if(annotationKeys != null && annotationKeys.length > 0){
						A annotation = subField.getAnnotation(anno);
						for(String annotationKey : annotationKeys){
							Object annotationValue = invovke(anno, annotation, annotationKey);
							annoMap.put(annotationKey, annotationValue);
						}
					}
				}else if(requiredAnno){ 
					continue OUT;
				}
			}
			properties.put(key, annoMap);
		}
		return properties;
	}
	
}
